package com.ease.util.configuration {
   import com.ease.util.MixedUtil;
   import com.ease.util.StringUtils;

   import flash.utils.Dictionary;

   import mx.utils.StringUtil;

   /**
    * Holds added Configuration objects and provides a means of unified access to system
    * wide configurations.
    */
   public class ConfigurationManager {

      /**
       * Singleton instance.
       */
      private static var _instance:ConfigurationManager;

      private var _configByKey:Dictionary/*ConfigurationProperty*/;

      /**
       * Factory method which returns a shared instance of ConfigurationManager.
       *
       * @return
       *    A shared instance of ConfigurationManager.
       */
      public static function get instance():ConfigurationManager {
         if (_instance == null) {
            _instance = new ConfigurationManager();
         }
         return _instance;
      }

      /**
       * Given key of a Configuration will return its value.
       *
       * @param key
       *    key of the Configuration whose value is desired.
       *
       * @return
       *    null or a valid straing value otherwise.
       */
      public function getConfigurationValue( key:String ):* {
         var config:ConfigurationProperty = findConfigurationByKey( key );
         if (config == null) {
            return null;
         }
         return config.value;
      }

      /**
       * Returns a configuration matching the specified key.
       *
       * @param key
       *    key of the desired Configuration.
       *
       * @return
       *
       */
      public function getConfiguration( key:String ):ConfigurationProperty {
         return findConfigurationByKey( key );
      }

      /**
       * All Configurations known to this instance.
       *
       * @return
       *    An array containing all the known configurations.
       */
      public function getConfigurations():Array/*ConfigurationProperty*/ {
         if (_configByKey == null) {
            return [];
         }
         return MixedUtil.getValues( _configByKey );
      }

      /**
       * Adds configuration so that they can retrieved later.
       *
       * @param configurations
       *    The configurations to be added.
       *
       * @param replaceExisting
       *    Will replace an already existing configuration with a matching key while
       * attempt to maintain the meta properties. For more information on meta proerties
       * look at Configuration.copyMissingMetaProperties.
       */
      public function addConfigurations(
         configurations:Array/*Configuration*/,
         replaceExisting:Boolean = false ):void {
         if (configurations == null) {
            return;
         }
         for each (var config:ConfigurationProperty in configurations) {
            addConfiguration( config, replaceExisting );
         }
      }

      /**
       * Loads configurations from the given Uri and adds them the the contained set
       * of configurations.
       *
       * @param uri
       *    The uri of the configurations file.
       *
       * @param callback
       *    <code>function(error:Error, callContext:Object=null)</code>.
       *
       * @param callContext
       *    The context object passed to the addConfigurationByUri method.
       */
      public function addConfigurationsByUri(
         uri:String,
         callback:Function,
         callContext:Object=null ):void {
         // TODO (manask): impl
      }

      /**
       * Adds a single configuration to an internal collection of Configurations.
       */
      private function addConfiguration(
         config:ConfigurationProperty,
         replaceExisting:Boolean ):Boolean {
         if (config == null || StringUtils.isNullorEmpty( config.key )) {
            return false;
         }
         var matchingConfig:ConfigurationProperty = findConfigurationByKey( config.key );
         if (!replaceExisting && matchingConfig != null) {
            ConfigurationProperty.copyMissingMetaProperties( matchingConfig, config );
            return false;
         }
         if (_configByKey == null) {
            _configByKey = new Dictionary();
         }
         if (matchingConfig != null) {
            ConfigurationProperty.copyMissingMetaProperties( config, matchingConfig );
         }
         _configByKey[ config.key ] = config;
         return true;
      }

      /**
       * Lookups a Configuration with the specified key. Will return null if no
       * matching configuration is found.
       */
      private function findConfigurationByKey( key:String ):ConfigurationProperty {
         if (StringUtils.isNullorEmpty( key ) || _configByKey == null) {
            return null;
         }
         return _configByKey[ key ];
      }
   }
}